Python 并发编程

1 为什么有操作系统

  • 操作系统: 位于底层和应用软件之间
  • 工作方式: 向下管理硬件 向上提供接口

1.1 操作系统进程切换:

  • 出现IO操作(像time.sleep之类的)
  • 固定时间(是操作系统控制的切换时间)

1.2进程的定义:

进程是一个程序一个数据集谁给你的动态执行过程
程序数据集(程序过程中使用的资源)、进程控制块(切换的时候 保存状态)

进程并发的时候,如果是一个应用程序,会把资源进行传递

进程是通过进程切换,是共享的资源,保存的的时候保存的是进程的资源集

  • 进程:
    资源管理单位(容器)
  • 线程:
    最小执行单位

1.3 并行和并发的区别:

串行是线程依次执行,运行期间独享内存
并行是每个线程占用一个内核
并发是有等待

python的多线程:
由于GIL,同一时刻,同一进程只能有一个线程被运行

但是可以实现多进程的并发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import threading
import time
s = time.time()
def listen():
print("listen")
time.sleep(3)
print("listen end")
def blog():
print("blog")
time.sleep(5)
print("blog end")
if __name__ == '__main__':
t1 = threading.Thread(target=listen) # 实例化
t2 = threading.Thread(target=blog)
t1.start() # 使用start方法调用
t2.start()
# t1.join()
# t2.join() # t1 t2 接收后 才开始主线程
print(time.time()-s) # 总的时间
print("main ending")
'''
结果:
listen
blog
0.0
main ending # 这个是3个运行完毕
listen end
blog end
可以证明的是实现了线程的切换
'''

使用join 方法,主线程会等待其余的结束后才运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import threading
import time
s = time.time()
def listen():
print("listen")
time.sleep(3)
print("listen end")
def blog():
print("blog")
time.sleep(5)
print("blog end")
if __name__ == '__main__':
t1 = threading.Thread(target=listen) # 实例化
t2 = threading.Thread(target=blog)
t1.start() # 使用start方法调用
t2.start()
t1.join()
t2.join() # t1 t2 接收后 才开始主线程
print(time.time()-s) # 总的时间
print("main ending")
'''
结果:
listen
blog
listen end
blog end
5.001286029815674 # 这里是线程2运行结束的时间
main ending # 最终运行主线程
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 方式2 使用的是run方法
自己定义的类取执行
```python
import time
import threading
class MyThread(threading.Thread): # 继承
def __init__(self, num):
threading.Thread.__init__(self)
self.num = num
def run(self):
print("the num is %s" % self.num)
time.sleep(3)
t1 = MyThread(56) # 参数
t2 = MyThread(78)
t1.start()
t2.start()
print("ending") # 这是主线程
'''
结果:
the num is 56
the num is 78
ending
'''

1.4 join方法

t.join() 主线程等待对象等待完

1
2
3
4
5
t1.start()
t2.start()
t1.join()
t2.join()

如果是: 下面就是串行

1
2
3
4
5
t1.start()
t1.join()
t2.start()
t2.join()

1.5 守护线程

setDaemon

主线程结束了,子线程就结束了

线程开启之后就不能控制

有自他的子线程t1,t2的时候,t2是守护线程,主线程先等待t1,主线程结束后,守护线程关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import threading
from time import ctime, sleep
import time
def Music(name):
print("Begin listening to {name}. {time}".format(name=name, time=ctime()))
sleep(3)
print("end listening {time}".format(time=ctime()))
def Blog(title):
print("Begin recording the {title}. {time}".format(
title=title, time=ctime()))
sleep(5)
print('end recording {time}'.format(time=ctime()))
threads = []
t1 = threading.Thread(target=Music, args=('music',))
t2 = threading.Thread(target=Blog, args=('blog',))
threads.append(t1)
threads.append(t2)
if __name__ == '__main__':
t1.setDaemon(True) # 把t2设置成守护线程
t2.setDaemon(True)
t1.start()
t2.start()
# for t in threads:
# t.setDaemon(True) #注意:一定在start之前设置
# t.start()
# for t in threads:
# t.join()
# t1.join()
# t2.join() # 考虑这三种join位置下的结果?
print("all over %s" % ctime())

1.6 其他的方法

1
2
3
4
5
6
7
8
9
Thread实例对象的方法
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。
threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

GIL 全局解释器锁

Global Interpreter Lock

加在CPython的解释器中,只有CPyhton中受到限制

阻止多线程并行
python的多线程:
由于GIL,同一时刻,同一进程只能有一个线程被运行

使用多进程的时候就可以突破这个限制

提高效率,用协程

  • 对于IO密集型的提高效率,不使用CPU

    1
    使用time.sleep的
  • 计算密集型,一直是在使用CPU,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import threading
import time
s = time.time()
def counter():
sum = 0
for i in range(50000000):
sum += i
if __name__ == '__main__':
# counter()
# counter()
t1 = threading.Thread(target=counter)
t2 = threading.Thread(target=counter)
t1.start()
t2.start()
t1.join()
t2.join()
print(time.time() - s)
'''
py2.7:
串行:10.5270001888
并发:15.7999999523
py3.:
串行:6.322361707687378
并发:6.371364593505859
Python3 的GIL已经进行了优化
'''

总结:
对于计算密集型的任务,Python多线程没有用
对于IO密集型的任务,Python多线程有用

Python使用多核: 开多进程
弊端: 内存开销大切换复杂

解决方案:

  • 使用协程,是单线程还可以自己控制切换的此时
  • IO多路复用   以后大多的的应用场景
  • 终极思路:C模块实现多线程
© 2018 Peter's Blog Center All Rights Reserved.
Theme by hiero